
require 'erb'
require 'base/context.rb'

# demoruby 本体．
# include して使う．
#
# Author:: krdlab
module Demoruby
  module ClassMethods
    def set_app_dir(dir)
      @app_dir = dir
    end
    def get_app_dir
      @app_dir
    end
  end

  def self.included(app)
    app.extend(ClassMethods)
  end

  # servlet 用メソッド

  def do_GET(req, res)
    dispatch(req, res)
  end

  def do_POST(req, res)
    dispatch(req, res)
  end

protected
  def get_app_dir
    self.class.get_app_dir
  end

  def get_view_dir
    File.join(get_app_dir, 'view')
  end

  def get_page_path(page)
    File.join(get_view_dir, "#{page}.rhtml")
  end

  def path_mapper(path)
    # {:app_name => 'name',
    #  :action => 'action',
    #  :args => [arg1, ...],
    #  :query => {param1 => value1, ...}}
    path =~ /\/([^\/\?]+)\/([^\/\?]+)(\/([^\?]+))?(\?(.*))?/
    m = {:app_name => $1, :action => $2}
    m[:args] = $4.split('/') if !$4.nil? && !$4.empty?
    if !$6.nil? && !$6.empty? then
      m[:query] = {}
      $6.split('&').each {|exp|
        m[:query].store(*exp.split('='))
      }
    end
    return m
  end

  def dispatch(req, res)
    m = path_mapper(req.path)
    # TODO ↓ これ移動しないと...
    res.content_type = 'text/html; charset=UTF-8;'
    begin
      @context = Context.new(req, res, m)
      __send__(@context.action, @context)
      if @context.rhtml_response? then
        res.body = eval_rhtml(@context.action, @context)
      end
    rescue => e
      handle_error(e)
    ensure
      @context = nil
    end
  end

  def forward_to(act_name)
    # TODO 標準の方法はないのか？
    @context.action = act_name
    __send__(@context.action, @context)
  end

  def redirect_to(act_name)
    uri = @context.req.request_uri.clone
    uri.path = '/' + [@context.app_name, act_name, *@context.path_args].join('/')
    @context.res.set_redirect(HTTPStatus::Found, uri)
  end

  def eval_rhtml(page, context)
    open(get_page_path(page)) {|f|
      ERB.new(f.read).result(binding)
    }
  end

  def handle_error(e)
    raise e
  end

  # 特殊メソッド (ERB スクリプトの中でしか使えない)
  def view_content(relative_path)
    "/#{@context.app_name}/view/#{relative_path}"
  end

  def style(args)
    if !args[:embed].nil? && args[:embed] == true then
      "<style type='text/css'>\n#{css_content(args[:content])}\n</style>"
    else
      "<link rel='stylesheet' href='#{view_content(args[:content])}' type='text/css' />"
    end
  end

  def css_content(file_name)
    open(File.join(get_view_dir, 'css', file_name)) {|f|
      f.read
    }
  end

  def javascript(path)
    "<script type='text/javascript' src='#{view_content(path)}'></script>"
  end

  def action_to(args)
    "#{args[:action]}"
  end

end

